using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Windows.Forms;

using Sandia.MVCFramework.Common;
using Sandia.MVCFramework.Data;
using Sandia.MVCFramework.DataAccessor;
using Sandia.MVCFramework.DataAnalyzer;
using Sandia.MVCFramework.DataStore;
using Sandia.MVCFramework.Plugin;
using Sandia.MVCFramework.View;
using Sandia.MVCFramework.ViewManager;


using NUnit.Framework;
using NUnit.Extensions.Forms;
using Sandia.MVCFramework.ExportTools;

namespace Sandia.MVCFramework.Test
{
    interface SomeInterface
    {
        void foobar();
    }

    class ParentType : IData
    {
    }

    class SomeType : ParentType, SomeInterface
    {
        #region SomeInterface Members

        public void foobar()
        {
            return;
        }

        #endregion
    }

    class TestSubscriber : ISubscriber
    {
        public bool GotTheData = false;
        private bool disposed = false;

        #region ISubscriber Members

        public IList<Type> DesiredSubscriptions
        {
            get
            {
                return new List<Type>();
            }
        }

        public void HandlePublishedData(DataPacket dataPacket)
        {
            this.GotTheData = true;
        }

        public bool IsDisposed
        {
            get { return disposed; }
        }

        public event EventHandler Disposed;

        #endregion

        #region IDisposable Members

        public void Dispose()
        {
            disposed = true;
            if (Disposed != null) Disposed(this, new EventArgs());
        }

        #endregion
    }

    [TestFixture]
    public class CommonTests: FailOnErrorLoggingTestFixture
    {
        [Test]
        public void StandardPublisherTest()
        {
            // Configure a standard publisher with a few
            // subscribers, excersing the ability to
            // subscribe to any of: a specific type, a general
            // parent type, or an interface implemented by a type
            StandardPublisher publisher = new StandardPublisher();
            TestSubscriber subscriberA = new TestSubscriber();
            TestSubscriber subscriberB = new TestSubscriber();
            TestSubscriber subscriberC = new TestSubscriber();
            TestSubscriber subscriberD = new TestSubscriber();
            publisher.Subscribe(subscriberA, typeof(SomeInterface));
            publisher.Subscribe(subscriberB, typeof(ParentType));
            publisher.Subscribe(subscriberC, typeof(SomeType));
            publisher.Subscribe(subscriberD, typeof(IData));

            // Check the subscriptions directly.
            Assert.IsTrue(publisher.DataSubscriptions.ContainsKey(typeof(SomeInterface)));
            Assert.AreEqual(1, publisher.DataSubscriptions[typeof(SomeInterface)].Count);
            Assert.IsTrue(publisher.DataSubscriptions[typeof(SomeInterface)].ContainsKey(subscriberA));
            Assert.IsTrue(publisher.DataSubscriptions.ContainsKey(typeof(ParentType)));
            Assert.AreEqual(1, publisher.DataSubscriptions[typeof(ParentType)].Count);
            Assert.IsTrue(publisher.DataSubscriptions[typeof(ParentType)].ContainsKey(subscriberB));
            Assert.IsTrue(publisher.DataSubscriptions.ContainsKey(typeof(SomeType)));
            Assert.AreEqual(1, publisher.DataSubscriptions[typeof(SomeType)].Count);
            Assert.IsTrue(publisher.DataSubscriptions[typeof(SomeType)].ContainsKey(subscriberC));
            Assert.IsTrue(publisher.DataSubscriptions.ContainsKey(typeof(IData)));
            Assert.AreEqual(1, publisher.DataSubscriptions[typeof(IData)].Count);
            Assert.IsTrue(publisher.DataSubscriptions[typeof(IData)].ContainsKey(subscriberD));
            
            // Publish data packet, then make sure it went
            // to everyone it was supposed to
            publisher.PublishData(new DataPacket(new SomeType(), ""));
            Assert.IsTrue(subscriberA.GotTheData);
            Assert.IsTrue(subscriberB.GotTheData);
            Assert.IsTrue(subscriberC.GotTheData);
            Assert.IsTrue(subscriberD.GotTheData);

            // Reset
            subscriberA.GotTheData = false;
            subscriberB.GotTheData = false;
            subscriberC.GotTheData = false;
            subscriberD.GotTheData = false;

            // Publish data packet, then make sure it went
            // to everyone it was supposed to
            publisher.PublishData(new DataPacket(new ParentType(), ""));
            Assert.IsFalse(subscriberA.GotTheData);
            Assert.IsTrue(subscriberB.GotTheData);
            Assert.IsFalse(subscriberC.GotTheData);
            Assert.IsTrue(subscriberD.GotTheData);

            // Reset
            subscriberA.GotTheData = false;
            subscriberB.GotTheData = false;
            subscriberC.GotTheData = false;
            subscriberD.GotTheData = false;

#if false // Publishing a null datapacket should have never been allowed
            // Check the null case...
            // Publish data packet, then make sure it went
            // to everyone it was supposed to
            publisher.PublishData(null);
            Assert.IsFalse(subscriberA.GotTheData);
            Assert.IsFalse(subscriberB.GotTheData);
            Assert.IsFalse(subscriberC.GotTheData);
            Assert.IsFalse(subscriberD.GotTheData);

            // Reset
            subscriberA.GotTheData = false;
            subscriberB.GotTheData = false;
            subscriberC.GotTheData = false;
            subscriberD.GotTheData = false;

            // Check the null case...
            // Publish data packet, then make sure it went
            // to everyone it was supposed to
            publisher.PublishData(new DataPacket(null, ""));
            Assert.IsFalse(subscriberA.GotTheData);
            Assert.IsFalse(subscriberB.GotTheData);
            Assert.IsFalse(subscriberC.GotTheData);
            Assert.IsFalse(subscriberD.GotTheData);
#endif

            // Test dispose
            subscriberA.Dispose();
            Assert.IsTrue(publisher.DataSubscriptions.ContainsKey(typeof(SomeInterface)));
            Assert.AreEqual(0, publisher.DataSubscriptions[typeof(SomeInterface)].Count);
            Assert.IsFalse(publisher.DataSubscriptions[typeof(SomeInterface)].ContainsKey(subscriberA));
        }

        [Test]
        public void StandardAppOptionsTest()
        {
            StandardAppOptions appOptions = new StandardAppOptions();
            Assert.IsFalse(appOptions.EnableLoggingView);
            Assert.IsNull(StandardAppOptions.GetALoggingView());
            Assert.IsNull(StandardAppOptions.GetLoggingViews());

            appOptions.EnableLoggingView = true;
            LoggingView loggingView = StandardAppOptions.GetALoggingView();
            Assert.IsNotNull(loggingView);
            List<LoggingView> loggingViews = StandardAppOptions.GetLoggingViews();
            Assert.IsNotNull(loggingViews);
            Assert.IsTrue(loggingViews.Contains(loggingView));
        }

        [Test]
        public void PinnableControlWrapperTest()
        {
            Control control = new Control();
            control.Name = "control";
            PinnableControlWrapper controlWrapper = new PinnableControlWrapper("control name", control);
            controlWrapper.Name = "controlWrapper";
            controlWrapper.ControlPinned += new OnControlPinnedChanged(controlWrapper_ControlPinned);
            controlWrapper.ControlUnpinned += new OnControlPinnedChanged(controlWrapper_ControlUnpinned);
            Form form = new Form();
            form.Controls.Add(controlWrapper);
            form.Show();

            ToolStripButtonTester pushpinBtnTester = new ToolStripButtonTester("pushpinBtn", form);
            pushpinBtnTester.Click();

            Assert.AreEqual(this.pinnedWrapper, controlWrapper);
            Assert.AreEqual(this.pinnedControl, control);

            pushpinBtnTester.Click();

            Assert.AreEqual(this.unpinnedWrapper, controlWrapper);
            Assert.AreEqual(this.unpinnedControl, control);
        }

        private Control unpinnedWrapper;

        private Control unpinnedControl;

        void controlWrapper_ControlUnpinned(
            Control wrapper, 
            Control control)
        {
            this.unpinnedWrapper = wrapper;
            this.unpinnedControl = control;
        }

        private Control pinnedWrapper;

        private Control pinnedControl;

        void controlWrapper_ControlPinned(
            Control wrapper, 
            Control control)
        {
            this.pinnedWrapper = wrapper;
            this.pinnedControl = control;
        }

        [Test]
        public void WindowHandleWrapperTest()
        {
            IntPtr testPtr = new IntPtr(1);

            WindowHandleWrapper windowHandleWrapper = 
                new WindowHandleWrapper(testPtr);

            Assert.AreEqual(testPtr, windowHandleWrapper.Handle);
        }

        [Test]
        public void FlyoutToolStripTest()
        {
            FlyoutToolStrip flyoutToolStrip = new FlyoutToolStrip();

            Control control1 = new Control();
            control1.Name = "control1";

            Control control2 = new Control();
            control1.Name = "control2";

            flyoutToolStrip.AddFlyoutControl("control1", control1);
            flyoutToolStrip.AddFlyoutControl("control2", control2);

            Form form = new Form();
            form.Controls.Add(flyoutToolStrip);
            form.Show();

            ToolStripButtonTester control1ButtonTester = 
                new ToolStripButtonTester("control1", form);
            Assert.AreEqual("control1", control1ButtonTester.Text);

            ToolStripButtonTester control2ButtonTester =
                new ToolStripButtonTester("control2", form);
            Assert.AreEqual("control2", control2ButtonTester.Text);
        }

        #region FileSystemTools

        [Test]
        public void CompareDirectoryInfosTest()
        {
            DirectoryInfo currentDirectory = new DirectoryInfo(Directory.GetCurrentDirectory());

            DirectoryInfo parentDirectory = currentDirectory.Parent.Parent;

            DirectoryInfo testDirectory1 = new DirectoryInfo(Path.Combine(parentDirectory.FullName, "TestDirectory1"));
            DirectoryInfo testDirectory2 = new DirectoryInfo(Path.Combine(parentDirectory.FullName, "TestDirectory2"));
            DirectoryInfo testDirectory3 = new DirectoryInfo(Path.Combine(parentDirectory.FullName, "TestDirectory3"));

            Assert.IsTrue(FileSystemTools.CompareDirectoryInfos(testDirectory1, testDirectory2), "The contents of the first directory and the second directory don't match.");
            Assert.IsFalse(FileSystemTools.CompareDirectoryInfos(testDirectory1, testDirectory3), "The contents of the first directory and the third directory match, but shouldn't.");
        }

        [Test]
        public void IsSubsetTest()
        {
            DirectoryInfo currentDirectory = new DirectoryInfo(Directory.GetCurrentDirectory());

            DirectoryInfo parentDirectory = currentDirectory.Parent.Parent;

            DirectoryInfo testDirectory1 = new DirectoryInfo(Path.Combine(parentDirectory.FullName, "TestDirectory1"));
            DirectoryInfo testDirectory2 = new DirectoryInfo(Path.Combine(parentDirectory.FullName, "TestDirectory2"));
            DirectoryInfo testDirectory3 = new DirectoryInfo(Path.Combine(parentDirectory.FullName, "TestDirectory3"));

            Assert.IsTrue(FileSystemTools.IsSubset(testDirectory1, testDirectory2),"TestDirectory1 is not a subset of TestDirectory2.");
            Assert.IsFalse(FileSystemTools.CompareDirectoryInfos(testDirectory1, testDirectory3),"");
        }

        /// <summary>
        /// Note, this code is also in PluginTests.cs
        /// </summary>
        /// <param name="dirA"></param>
        /// <param name="dirB"></param>
        void CompareDirectories(string dirA, string dirB)
        {
            DirectoryInfo dir1 = new DirectoryInfo(dirA);
            DirectoryInfo dir2 = new DirectoryInfo(dirB);

            // Get a list of all of the entries (files and directories) in the source and the target
            //
            FileSystemInfo[] entries1 = dir1.GetFileSystemInfos();
            FileSystemInfo[] entries2 = dir2.GetFileSystemInfos();

            // While we can use the built-in iterator for one list, we have to create a "manual" iterator for the other list
            //
            System.Collections.IEnumerator iter2 = entries2.GetEnumerator();

            // Iterate through the entries in the list from the source
            //
            foreach (FileSystemInfo anEntry1 in entries1)
            {
                if (anEntry1.Name.CompareTo("CVS") == 0) continue; // skip the CVS folder
                // It is important to note that we must not disturb the second iterator here.

            RetryEntry2: // a sort of loop construct, the Assert will cause an exit

                // Try to move to the next entry in the list from the target
                //
                bool yesNext = iter2.MoveNext();

                // Test to ensure that there was indeed a next entry in the target's list
                //
                Assert.IsTrue(yesNext, "Inconsistent entries in both source and target.");

                // Get the entry
                //
                FileSystemInfo anEntry2 = (FileSystemInfo)iter2.Current;

                if (anEntry2.Name.CompareTo("CVS") == 0) goto RetryEntry2; //skip the CVS folder

                // Test to ensure that these entries from the source and the target correspond
                //
                Assert.AreEqual(anEntry1.Name, anEntry2.Name, "Directory entries don't match exactly.");

                if ( (anEntry1.Attributes & FileAttributes.Directory) == FileAttributes.Directory && 
                     (anEntry2.Attributes & FileAttributes.Directory) == FileAttributes.Directory)
                {
                    this.CompareDirectories( Path.Combine(dirA,anEntry1.ToString()), Path.Combine(dirB,anEntry2.ToString()) ); //recurse
                }
            }
        }

        [Test]
        public void SynchronizeDirectoriesTest()
        {
            DirectoryInfo currentDirectory = new DirectoryInfo(Directory.GetCurrentDirectory());

            DirectoryInfo parentDirectory = currentDirectory.Parent.Parent;

            DirectoryInfo testDirectory1 = new DirectoryInfo(Path.Combine(parentDirectory.FullName, "TestDirectory1"));
            DirectoryInfo testDirectory4 = new DirectoryInfo(Path.Combine(parentDirectory.FullName, "TestDirectory4"));
            DirectoryInfo testDirectory5 = new DirectoryInfo(Path.Combine(parentDirectory.FullName, "TestDirectory5"));

            // Note, TestDirectory4 is not being used because it causes a CVS "update/chage" as a 
            // side effect of running this test.

            // Copy from 4 to 5
            //
            FileSystemTools.Copy(testDirectory4, testDirectory5, true); // this is okay because it doesn't effect 4

            FileSystemTools.SynchronizeDirectories(testDirectory1, testDirectory5);

            CompareDirectories( testDirectory1.ToString(), testDirectory5.ToString() );

            // Empty the destination directory
            //
            testDirectory5.Delete(true); //recursively delete
        }

        [Test]
        public void CopyTest()
        {
            DirectoryInfo currentDirectory = new DirectoryInfo(Directory.GetCurrentDirectory());

            DirectoryInfo parentDirectory = currentDirectory.Parent.Parent;

            DirectoryInfo testDirectory1 = new DirectoryInfo(Path.Combine(parentDirectory.FullName, "TestDirectory1"));
            DirectoryInfo testDirectory5 = new DirectoryInfo(Path.Combine(parentDirectory.FullName, "TestDirectory5"));

            // Ensure the destination directory doesn't exist
            //
            if( Directory.Exists(testDirectory5.ToString()) )
            {
                testDirectory5.Delete(true); //recursively delete
            }

            // Copy from the source to the target - object of our test
            //
            FileSystemTools.Copy(testDirectory1, testDirectory5, true);

            this.CompareDirectories(testDirectory1.ToString(), testDirectory5.ToString());

            // Delete the copied directory
            //
            testDirectory5.Delete(true);
        }


        [Test]
        public void ClearDirectoryTest()
        {
            DirectoryInfo currentDirectory = new DirectoryInfo(Directory.GetCurrentDirectory());

            DirectoryInfo parentDirectory = currentDirectory.Parent.Parent;

            DirectoryInfo testDirectory1 = new DirectoryInfo(Path.Combine(parentDirectory.FullName, "TestDirectory1"));
            DirectoryInfo testDirectory5 = new DirectoryInfo(Path.Combine(parentDirectory.FullName, "TestDirectory5"));

#if true // Synchronize doesn't work if the directory doesn't exist - ToDo, BST talk to JT
            // Ensure the destination directory doesn't exist
            //
            if (Directory.Exists(testDirectory5.ToString()))
            {
                testDirectory5.Delete(true); //recursively delete
            }

            // Copy from the source to the target - object of our test
            //
            FileSystemTools.Copy(testDirectory1, testDirectory5, true);
#else
            FileSystemTools.SynchronizeDirectories(testDirectory1, testDirectory5);
#endif

            FileSystemTools.ClearDirectory(new DirectoryInfo(testDirectory5.FullName));

            Assert.AreEqual(0, testDirectory5.GetFileSystemInfos().Length);
        }

        #endregion

        #region ArrayTools

        [Test]
        public void CompareArraysTest()
        {
            DirectoryInfo[] array1 = new DirectoryInfo[1];
            DirectoryInfo[] array2 = new DirectoryInfo[2];

            Assert.IsFalse(ArrayTools<DirectoryInfo>.CompareArrays(array1, array2, new DirectoryInfoComparer()));

            array1 = new DirectoryInfo[2];

            DirectoryInfo currentDirectory = new DirectoryInfo(Directory.GetCurrentDirectory());

            DirectoryInfo parentDirectory = currentDirectory.Parent.Parent;

            DirectoryInfo testDirectory1 = new DirectoryInfo(Path.Combine(parentDirectory.FullName, "TestDirectory1"));
            DirectoryInfo testDirectory2 = new DirectoryInfo(Path.Combine(parentDirectory.FullName, "TestDirectory2"));
            DirectoryInfo testDirectory3 = new DirectoryInfo(Path.Combine(parentDirectory.FullName, "TestDirectory3"));
            DirectoryInfo testDirectory4 = new DirectoryInfo(Path.Combine(parentDirectory.FullName, "TestDirectory4"));

            array1[0] = testDirectory1;
            array1[1] = testDirectory2;

            array2[0] = testDirectory3;
            array2[1] = testDirectory4;

            Assert.IsFalse(ArrayTools<DirectoryInfo>.CompareArrays(array1, array2, new DirectoryInfoComparer()));

            array2[0] = testDirectory1;
            array2[1] = testDirectory2;

            Assert.IsTrue(ArrayTools<DirectoryInfo>.CompareArrays(array1, array2, new DirectoryInfoComparer()));
        }

        [Test]
        public void ArrayIsSubsetTest()
        {
            DirectoryInfo[] array1 = new DirectoryInfo[2];
            DirectoryInfo[] array2 = new DirectoryInfo[2];

            DirectoryInfo currentDirectory = new DirectoryInfo(Directory.GetCurrentDirectory());

            DirectoryInfo parentDirectory = currentDirectory.Parent.Parent;

            DirectoryInfo testDirectory1 = new DirectoryInfo(Path.Combine(parentDirectory.FullName, "TestDirectory1"));
            DirectoryInfo testDirectory2 = new DirectoryInfo(Path.Combine(parentDirectory.FullName, "TestDirectory2"));
            DirectoryInfo testDirectory3 = new DirectoryInfo(Path.Combine(parentDirectory.FullName, "TestDirectory3"));
            DirectoryInfo testDirectory4 = new DirectoryInfo(Path.Combine(parentDirectory.FullName, "TestDirectory4"));

            array1[0] = testDirectory1;
            array1[1] = testDirectory2;

            array2[0] = testDirectory3;
            array2[1] = testDirectory4;

            Assert.IsFalse(ArrayTools<DirectoryInfo>.IsSubset(array1, array2, new DirectoryInfoComparer()),"The first array is a subset of the second, but it shouldn't be.");

            array2[0] = testDirectory1;
            array2[1] = testDirectory2;

            Assert.IsTrue(ArrayTools<DirectoryInfo>.IsSubset(array1, array2, new DirectoryInfoComparer()),"The first array is not a subset of the second.");

            array1 = new DirectoryInfo[1];
            array1[0] = testDirectory1;

            Assert.IsTrue(ArrayTools<DirectoryInfo>.IsSubset(array1, array2, new DirectoryInfoComparer()),"The first array is not a subset of the second.");

            array1 = new DirectoryInfo[3];
            array1[0] = testDirectory1;
            array1[1] = testDirectory2;
            array1[2] = testDirectory3;

            Assert.IsFalse(ArrayTools<DirectoryInfo>.IsSubset(array1, array2, new DirectoryInfoComparer()),"The first array is a subset of the second, but it shouldn't be.");
        }

        [Test]
        public void GetSubsetDifferencesTest()
        {
            DirectoryInfo[] array1 = new DirectoryInfo[2];
            DirectoryInfo[] array2 = new DirectoryInfo[2];

            DirectoryInfoComparer comparer = new DirectoryInfoComparer();

            DirectoryInfo currentDirectory = new DirectoryInfo(Directory.GetCurrentDirectory());

            DirectoryInfo parentDirectory = currentDirectory.Parent.Parent;

            DirectoryInfo testDirectory1 = new DirectoryInfo(Path.Combine(parentDirectory.FullName, "TestDirectory1"));
            DirectoryInfo testDirectory2 = new DirectoryInfo(Path.Combine(parentDirectory.FullName, "TestDirectory2"));
            DirectoryInfo testDirectory3 = new DirectoryInfo(Path.Combine(parentDirectory.FullName, "TestDirectory3"));
            DirectoryInfo testDirectory4 = new DirectoryInfo(Path.Combine(parentDirectory.FullName, "TestDirectory4"));

            array1[0] = testDirectory1;
            array1[1] = testDirectory2;

            array2[0] = testDirectory3;
            array2[1] = testDirectory4;

            List<DirectoryInfo> result = ArrayTools<DirectoryInfo>.GetSubsetDifferences(array1, array2, new DirectoryInfoComparer());

            Assert.AreEqual(2, result.Count);
            Assert.AreEqual(0, comparer.Compare(result[0], testDirectory1));
            Assert.AreEqual(0, comparer.Compare(result[1], testDirectory2));

            array2[0] = testDirectory1;
            array2[1] = testDirectory2;

            result = ArrayTools<DirectoryInfo>.GetSubsetDifferences(array1, array2, new DirectoryInfoComparer());

            Assert.AreEqual(0, result.Count, "There shouldn't be any differences, but there are.");
        }

        #endregion

        [Test]
        public void DirectoryInfoComparerTest()
        {
            DirectoryInfoComparer comparer = new DirectoryInfoComparer();

            DirectoryInfo currentDirectory = new DirectoryInfo(Directory.GetCurrentDirectory());

            DirectoryInfo parentDirectory = currentDirectory.Parent.Parent;

            DirectoryInfo testDirectory1 = new DirectoryInfo(Path.Combine(parentDirectory.FullName, "TestDirectory1"));
            DirectoryInfo testDirectory2 = new DirectoryInfo(Path.Combine(parentDirectory.FullName, "TestDirectory1"));
            DirectoryInfo testDirectory3 = new DirectoryInfo(Path.Combine(parentDirectory.FullName, "TestDirectory2"));

            Assert.AreEqual(String.Compare(testDirectory1.Name, testDirectory2.Name), comparer.Compare(testDirectory1, testDirectory2), "The directory names (1&2) don't match.");
            Assert.AreEqual(String.Compare(testDirectory1.Name, testDirectory3.Name), comparer.Compare(testDirectory1, testDirectory3), "The directory names (1&3) don't match.");
        }

        [Test]
        public void ScheduleDependencyLoadingTest()
        {
            Dictionary<string, IPlugin> availablePlugins =
                new Dictionary<string, IPlugin>();

            Stack<string> selectedPlugins = new Stack<string>();

            // Test the case when there aren't any plug-ins
            //
            Queue<IPlugin> result = PluginTools.ScheduleDependencyLoading(availablePlugins, selectedPlugins);
            Assert.IsEmpty(result, "The resultant plug-in list should have been empty, but wasn't.");

            APlugin aPlugin = new APlugin();
            BPlugin bPlugin = new BPlugin();
            CPlugin cPlugin = new CPlugin();
            DPlugin dPlugin = new DPlugin();
            EPlugin ePlugin = new EPlugin();

            availablePlugins[aPlugin.ID] = aPlugin;
            availablePlugins[bPlugin.ID] = bPlugin;
            availablePlugins[cPlugin.ID] = cPlugin;
            availablePlugins[dPlugin.ID] = dPlugin;
            availablePlugins[ePlugin.ID] = ePlugin;

            selectedPlugins.Push(aPlugin.ID);
            selectedPlugins.Push(bPlugin.ID);
            selectedPlugins.Push(cPlugin.ID);
            selectedPlugins.Push(dPlugin.ID);
            selectedPlugins.Push(ePlugin.ID);

            //Queue<IPlugin> result = PluginTools.ScheduleDependencyLoading(availablePlugins, selectedPlugins);
            result = PluginTools.ScheduleDependencyLoading(availablePlugins, selectedPlugins);

            Assert.AreEqual(cPlugin, result.Dequeue());
            Assert.AreEqual(dPlugin, result.Dequeue());
            Assert.AreEqual(ePlugin, result.Dequeue());
            Assert.AreEqual(bPlugin, result.Dequeue());
            Assert.AreEqual(aPlugin, result.Dequeue());

            selectedPlugins.Clear();
            selectedPlugins.Push(aPlugin.ID);
            selectedPlugins.Push(ePlugin.ID);
            selectedPlugins.Push(cPlugin.ID);
            selectedPlugins.Push(dPlugin.ID);
            selectedPlugins.Push(bPlugin.ID);

            result = PluginTools.ScheduleDependencyLoading(availablePlugins, selectedPlugins);

            Assert.AreEqual(cPlugin, result.Dequeue());
            Assert.AreEqual(dPlugin, result.Dequeue());
            Assert.AreEqual(ePlugin, result.Dequeue());
            Assert.AreEqual(bPlugin, result.Dequeue());
            Assert.AreEqual(aPlugin, result.Dequeue());

            selectedPlugins.Clear();
            selectedPlugins.Push(dPlugin.ID);
            selectedPlugins.Push(ePlugin.ID);
            selectedPlugins.Push(bPlugin.ID);
            selectedPlugins.Push(aPlugin.ID);

            result = PluginTools.ScheduleDependencyLoading(availablePlugins, selectedPlugins);

            Assert.AreEqual(cPlugin, result.Dequeue());
            Assert.AreEqual(dPlugin, result.Dequeue());
            Assert.AreEqual(ePlugin, result.Dequeue());
            Assert.AreEqual(bPlugin, result.Dequeue());
            Assert.AreEqual(aPlugin, result.Dequeue());

            selectedPlugins.Clear();
            selectedPlugins.Push(aPlugin.ID);
            selectedPlugins.Push(ePlugin.ID);
            selectedPlugins.Push(cPlugin.ID);
            selectedPlugins.Push(dPlugin.ID);
            selectedPlugins.Push(bPlugin.ID);

            availablePlugins.Remove(cPlugin.ID);

            result = PluginTools.ScheduleDependencyLoading(availablePlugins, selectedPlugins);

            Assert.AreEqual(dPlugin, result.Dequeue());
            Assert.AreEqual(ePlugin, result.Dequeue());
            Assert.AreEqual(bPlugin, result.Dequeue());
            Assert.AreEqual(aPlugin, result.Dequeue());
        }

        private class APlugin : IPlugin
        {
            #region IPlugin Members

            private List<IDataAccessor> dataAccessors;
            private List<IDataAnalyzer> dataAnalyzers;
            private List<IDataStore> dataStores;

            private List<IViewManager> viewManagers;

            private Dictionary<Type, List<IRequestProcessor>> requestRoutings;
            private Dictionary<Type, List<ISubscriber>> subscriptions;



            public void Init(IRequestRouter requestRouter, IPublisher publisher)
            {
                dataAccessors = new List<IDataAccessor>();
                dataAnalyzers = new List<IDataAnalyzer>();
                dataStores = new List<IDataStore>();
                viewManagers = new List<IViewManager>();
                requestRoutings = new Dictionary<Type, List<IRequestProcessor>>();
                subscriptions = new Dictionary<Type, List<ISubscriber>>();
            }

            public const string PLUGIN_ID = "APlugin";

            public string ID
            {
                get { return APlugin.PLUGIN_ID; }
            }

            public string DisplayName
            {
                get { throw new Exception("The method or operation is not implemented."); }
            }

            public long LoaderVersionToUse
            {
                get { throw new Exception("The method or operation is not implemented."); }
            }

            public List<string> Dependencies
            {
                get
                {
                    List<string> dependencies = new List<string>();
                    dependencies.Add(BPlugin.PLUGIN_ID);
                    dependencies.Add(CPlugin.PLUGIN_ID);
                    dependencies.Add(DPlugin.PLUGIN_ID);
                    return dependencies;
                }
            }

            public List<Sandia.MVCFramework.DataAccessor.IDataAccessor> DataAccessors
            {
                get { return dataAccessors; }
            }

            public List<Sandia.MVCFramework.DataAnalyzer.IDataAnalyzer> DataAnalyzers
            {
                get { return dataAnalyzers; }
            }

            public List<Sandia.MVCFramework.DataStore.IDataStore> DataStores
            {
                get { return dataStores; }
            }

            public Dictionary<Type, List<IRequestProcessor>> RequestRoutings
            {
                get { return requestRoutings; }
            }

            public Dictionary<Type, List<ISubscriber>> Subscriptions
            {
                get { return subscriptions; }
            }

            public List<Sandia.MVCFramework.ViewManager.IViewManager> ViewManagers
            {
                get { return viewManagers; }
            }

            // TODO JTM - Need to implement these...
            public List<IExporter> Exporters
            {
                get { throw new Exception("The method or operation is not implemented."); }
            }

            public List<IImporter> Importers
            {
                get { throw new Exception("The method or operation is not implemented."); }
            }

            #endregion

            #region IStartable Members

            public void Start()
            {
                throw new Exception("The method or operation is not implemented.");
            }

            #endregion

            #region IRequestProcessor Members

            public IList<Type> DesiredRequests
            {
                get { throw new Exception("The method or operation is not implemented."); }
            }

            public void ProcessRequest(Sandia.MVCFramework.Request.IRequest request)
            {
                throw new Exception("The method or operation is not implemented.");
            }

            public bool IsDisposed
            {
                get { throw new Exception("The method or operation is not implemented."); }
            }

            public event EventHandler Disposed;

            #endregion

            #region IDisposable Members

            public void Dispose()
            {
                throw new Exception("The method or operation is not implemented.");
            }

            #endregion

            #region IDataPacketProducer Members

            public void InitDataPacketProducer(IPublisher publisher)
            {
                throw new Exception("The method or operation is not implemented.");
            }

            #endregion
        }

        private class BPlugin : IPlugin
        {
            #region IPlugin Members

            public void Init(IRequestRouter requestRouter, IPublisher publisher)
            {
                throw new Exception("The method or operation is not implemented.");
            }

            public const string PLUGIN_ID = "BPlugin";

            public string ID
            {
                get { return BPlugin.PLUGIN_ID; }
            }

            public string DisplayName
            {
                get { throw new Exception("The method or operation is not implemented."); }
            }

            public long LoaderVersionToUse
            {
                get { throw new Exception("The method or operation is not implemented."); }
            }

            public List<string> Dependencies
            {
                get
                {
                    List<string> dependencies = new List<string>();
                    dependencies.Add(DPlugin.PLUGIN_ID);
                    dependencies.Add(EPlugin.PLUGIN_ID);
                    return dependencies;
                }
            }

            public List<Sandia.MVCFramework.DataAccessor.IDataAccessor> DataAccessors
            {
                get { throw new Exception("The method or operation is not implemented."); }
            }

            public List<Sandia.MVCFramework.DataAnalyzer.IDataAnalyzer> DataAnalyzers
            {
                get { throw new Exception("The method or operation is not implemented."); }
            }

            public List<Sandia.MVCFramework.DataStore.IDataStore> DataStores
            {
                get { throw new Exception("The method or operation is not implemented."); }
            }

            public Dictionary<Type, List<IRequestProcessor>> RequestRoutings
            {
                get { throw new Exception("The method or operation is not implemented."); }
            }

            public Dictionary<Type, List<ISubscriber>> Subscriptions
            {
                get { throw new Exception("The method or operation is not implemented."); }
            }

            public List<Sandia.MVCFramework.ViewManager.IViewManager> ViewManagers
            {
                get { throw new Exception("The method or operation is not implemented."); }
            }

            // TODO JTM - Need to implement these...
            public List<IExporter> Exporters
            {
                get { throw new Exception("The method or operation is not implemented."); }
            }

            public List<IImporter> Importers
            {
                get { throw new Exception("The method or operation is not implemented."); }
            }

            #endregion

            #region IStartable Members

            public void Start()
            {
                throw new Exception("The method or operation is not implemented.");
            }

            #endregion

            #region IRequestProcessor Members

            public IList<Type> DesiredRequests
            {
                get { throw new Exception("The method or operation is not implemented."); }
            }

            public void ProcessRequest(Sandia.MVCFramework.Request.IRequest request)
            {
                throw new Exception("The method or operation is not implemented.");
            }

            public bool IsDisposed
            {
                get { throw new Exception("The method or operation is not implemented."); }
            }

            public event EventHandler Disposed;

            #endregion

            #region IDisposable Members

            public void Dispose()
            {
                throw new Exception("The method or operation is not implemented.");
            }

            #endregion

            #region IDataPacketProducer Members

            public void InitDataPacketProducer(IPublisher publisher)
            {
                throw new Exception("The method or operation is not implemented.");
            }

            #endregion
        }

        private class CPlugin : IPlugin
        {
            #region IPlugin Members

            public void Init(IRequestRouter requestRouter, IPublisher publisher)
            {
                throw new Exception("The method or operation is not implemented.");
            }

            public const string PLUGIN_ID = "CPlugin";

            public string ID
            {
                get { return CPlugin.PLUGIN_ID; }
            }

            public string DisplayName
            {
                get { throw new Exception("The method or operation is not implemented."); }
            }

            public long LoaderVersionToUse
            {
                get { throw new Exception("The method or operation is not implemented."); }
            }

            public List<string> Dependencies
            {
                get
                {
                    List<string> dependencies = new List<string>();
                    return dependencies;
                }
            }

            public List<Sandia.MVCFramework.DataAccessor.IDataAccessor> DataAccessors
            {
                get { throw new Exception("The method or operation is not implemented."); }
            }

            public List<Sandia.MVCFramework.DataAnalyzer.IDataAnalyzer> DataAnalyzers
            {
                get { throw new Exception("The method or operation is not implemented."); }
            }

            public List<Sandia.MVCFramework.DataStore.IDataStore> DataStores
            {
                get { throw new Exception("The method or operation is not implemented."); }
            }

            public Dictionary<Type, List<IRequestProcessor>> RequestRoutings
            {
                get { throw new Exception("The method or operation is not implemented."); }
            }

            public Dictionary<Type, List<ISubscriber>> Subscriptions
            {
                get { throw new Exception("The method or operation is not implemented."); }
            }

            public List<Sandia.MVCFramework.ViewManager.IViewManager> ViewManagers
            {
                get { throw new Exception("The method or operation is not implemented."); }
            }

            // TODO JTM - Need to implement these...
            public List<IExporter> Exporters
            {
                get { throw new Exception("The method or operation is not implemented."); }
            }

            public List<IImporter> Importers
            {
                get { throw new Exception("The method or operation is not implemented."); }
            }

            #endregion

            #region IStartable Members

            public void Start()
            {
                throw new Exception("The method or operation is not implemented.");
            }

            #endregion

            #region IRequestProcessor Members

            public IList<Type> DesiredRequests
            {
                get { throw new Exception("The method or operation is not implemented."); }
            }

            public void ProcessRequest(Sandia.MVCFramework.Request.IRequest request)
            {
                throw new Exception("The method or operation is not implemented.");
            }

            public bool IsDisposed
            {
                get { throw new Exception("The method or operation is not implemented."); }
            }

            public event EventHandler Disposed;

            #endregion

            #region IDisposable Members

            public void Dispose()
            {
                throw new Exception("The method or operation is not implemented.");
            }

            #endregion

            #region IDataPacketProducer Members

            public void InitDataPacketProducer(IPublisher publisher)
            {
                throw new Exception("The method or operation is not implemented.");
            }

            #endregion
        }

        private class DPlugin : IPlugin
        {
            #region IPlugin Members

            public void Init(IRequestRouter requestRouter, IPublisher publisher)
            {
                throw new Exception("The method or operation is not implemented.");
            }

            public const string PLUGIN_ID = "DPlugin";

            public string ID
            {
                get { return DPlugin.PLUGIN_ID; }
            }

            public string DisplayName
            {
                get { throw new Exception("The method or operation is not implemented."); }
            }

            public long LoaderVersionToUse
            {
                get { throw new Exception("The method or operation is not implemented."); }
            }

            public List<string> Dependencies
            {
                get
                {
                    List<string> dependencies = new List<string>();
                    dependencies.Add(CPlugin.PLUGIN_ID);
                    return dependencies;
                }
            }

            public List<Sandia.MVCFramework.DataAccessor.IDataAccessor> DataAccessors
            {
                get { throw new Exception("The method or operation is not implemented."); }
            }

            public List<Sandia.MVCFramework.DataAnalyzer.IDataAnalyzer> DataAnalyzers
            {
                get { throw new Exception("The method or operation is not implemented."); }
            }

            public List<Sandia.MVCFramework.DataStore.IDataStore> DataStores
            {
                get { throw new Exception("The method or operation is not implemented."); }
            }

            public Dictionary<Type, List<IRequestProcessor>> RequestRoutings
            {
                get { throw new Exception("The method or operation is not implemented."); }
            }

            public Dictionary<Type, List<ISubscriber>> Subscriptions
            {
                get { throw new Exception("The method or operation is not implemented."); }
            }

            public List<Sandia.MVCFramework.ViewManager.IViewManager> ViewManagers
            {
                get { throw new Exception("The method or operation is not implemented."); }
            }

            // TODO JTM - Need to implement these...
            public List<IExporter> Exporters
            {
                get { throw new Exception("The method or operation is not implemented."); }
            }

            public List<IImporter> Importers
            {
                get { throw new Exception("The method or operation is not implemented."); }
            }

            #endregion

            #region IStartable Members

            public void Start()
            {
                throw new Exception("The method or operation is not implemented.");
            }

            #endregion

            #region IRequestProcessor Members

            public IList<Type> DesiredRequests
            {
                get { throw new Exception("The method or operation is not implemented."); }
            }

            public void ProcessRequest(Sandia.MVCFramework.Request.IRequest request)
            {
                throw new Exception("The method or operation is not implemented.");
            }

            public bool IsDisposed
            {
                get { throw new Exception("The method or operation is not implemented."); }
            }

            public event EventHandler Disposed;

            #endregion

            #region IDisposable Members

            public void Dispose()
            {
                throw new Exception("The method or operation is not implemented.");
            }

            #endregion

            #region IDataPacketProducer Members

            public void InitDataPacketProducer(IPublisher publisher)
            {
                throw new Exception("The method or operation is not implemented.");
            }

            #endregion
        }

        private class EPlugin : IPlugin
        {
            #region IPlugin Members

            public void Init(IRequestRouter requestRouter, IPublisher publisher)
            {
                throw new Exception("The method or operation is not implemented.");
            }

            public const string PLUGIN_ID = "EPlugin";

            public string ID
            {
                get { return EPlugin.PLUGIN_ID; }
            }

            public string DisplayName
            {
                get { throw new Exception("The method or operation is not implemented."); }
            }

            public long LoaderVersionToUse
            {
                get { throw new Exception("The method or operation is not implemented."); }
            }

            public List<string> Dependencies
            {
                get
                {
                    List<string> dependencies = new List<string>();
                    dependencies.Add(CPlugin.PLUGIN_ID);
                    dependencies.Add(DPlugin.PLUGIN_ID);
                    return dependencies;
                }
            }

            public List<Sandia.MVCFramework.DataAccessor.IDataAccessor> DataAccessors
            {
                get { throw new Exception("The method or operation is not implemented."); }
            }

            public List<Sandia.MVCFramework.DataAnalyzer.IDataAnalyzer> DataAnalyzers
            {
                get { throw new Exception("The method or operation is not implemented."); }
            }

            public List<Sandia.MVCFramework.DataStore.IDataStore> DataStores
            {
                get { throw new Exception("The method or operation is not implemented."); }
            }

            public Dictionary<Type, List<IRequestProcessor>> RequestRoutings
            {
                get { throw new Exception("The method or operation is not implemented."); }
            }

            public Dictionary<Type, List<ISubscriber>> Subscriptions
            {
                get { throw new Exception("The method or operation is not implemented."); }
            }

            public List<Sandia.MVCFramework.ViewManager.IViewManager> ViewManagers
            {
                get { throw new Exception("The method or operation is not implemented."); }
            }

            // TODO JTM - Need to implement these...
            public List<IExporter> Exporters
            {
                get { throw new Exception("The method or operation is not implemented."); }
            }

            public List<IImporter> Importers
            {
                get { throw new Exception("The method or operation is not implemented."); }
            }

            #endregion

            #region IStartable Members

            public void Start()
            {
                throw new Exception("The method or operation is not implemented.");
            }

            #endregion

            #region IRequestProcessor Members

            public IList<Type> DesiredRequests
            {
                get { throw new Exception("The method or operation is not implemented."); }
            }

            public void ProcessRequest(Sandia.MVCFramework.Request.IRequest request)
            {
                throw new Exception("The method or operation is not implemented.");
            }

            public bool IsDisposed
            {
                get { throw new Exception("The method or operation is not implemented."); }
            }

            public event EventHandler Disposed;

            #endregion

            #region IDisposable Members

            public void Dispose()
            {
                throw new Exception("The method or operation is not implemented.");
            }

            #endregion

            #region IDataPacketProducer Members

            public void InitDataPacketProducer(IPublisher publisher)
            {
                throw new Exception("The method or operation is not implemented.");
            }

            #endregion
        }
    }
}
